package analisador;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.StringTokenizer;
import util.Arquivos;

public class MiniMundo {

    //map com todos os tokens com o número de ocorrência
    static Map<String, Integer> mapTokens;
    static List<String> listaOrdenada;
    static Map<String, Set<Integer>> listaAdjacenciaAutor = new HashMap<String, Set<Integer>>();
    static Map<String, Set<Integer>> mapMensagensAutor = new HashMap<String, Set<Integer>>();
    static Connection con;
    static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    static int branco = 0;

    public static void main(String[] args) throws Exception {

        //-1 toda base
        int qtde = -1;
        // gera o MiniMundo e as mensagens com saida em arquivos
        // na pasta arquivosGerados
        MiniMundo.gerar(qtde, "hadoop"); // qtde de registro e padrao do nome dos arquivos
        System.out.printf("Qtde tokens do MiniMundo: %d %n", MiniMundo.mapTokens.size());
        System.out.printf("Mensagens em branco: %d %n", MiniMundo.branco);
    }

    public static void conectar() throws Exception {
        con = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/hadoop",
                "root",
                "root");
//        con = DriverManager.getConnection(
//                "jdbc:mysql://172.16.255.144:3306/hadoop",
//                "root",
//                "root");
    }

    public static void desconectar() throws Exception {
        con.close();
    }

    public static void gerar(int qtde, String nome) throws Exception {
        conectar();

        System.out.printf("[%d]Consulta na base...", qtde);
        long tInicial = System.currentTimeMillis();
        Statement stmt = con.createStatement();
        ResultSet rs;
        if (qtde == -1) {
            rs = stmt.executeQuery("SELECT id,tokens,autor FROM comentario");
        } else {
            rs = stmt.executeQuery("SELECT id,tokens,autor FROM comentario LIMIT 0, " + qtde);
        }
        long tFinal = System.currentTimeMillis();
        System.out.printf("%dms %n", (tFinal - tInicial));

        geraMiniMundo(rs, nome);
        geraMensagens(rs, nome);
        agrupaMensagensAutor(nome);
        
        rs.close();
        desconectar();
    }

    private static void geraMiniMundo(ResultSet rs, String nome) throws SQLException {
        long tInicial;
        long tFinal;
        System.out.println("\nGerando MiniMundo...");
        long cont = 0;
        mapTokens = new HashMap<String, Integer>();
        tInicial = System.currentTimeMillis();
        while (rs.next()) {
            long id = rs.getLong("id");
            String tokens = rs.getString("tokens");
            insertMiniMundo(id, tokens);
            if (++cont % 1000 == 0) {
                tFinal = System.currentTimeMillis();
                System.out.printf("%d: %dms %n", cont, (tFinal - tInicial));
                tInicial = tFinal;
                System.gc();
            }
        }
        //lista ordenada dos tokens
        listaOrdenada = new ArrayList<String>(mapTokens.keySet());
        Collections.sort(listaOrdenada);

        StringBuilder b = new StringBuilder();
        for (String s : listaOrdenada) {
            b.append(s);
            b.append("\n");
        }
        String nomeArquivo = String.format("arquivosGerados/%sMiniMundo.txt", nome);
        Arquivos.gravar(nomeArquivo, b.toString(),false);
        //Arquivos.gravar("universoOrdenado.txt", listaOrdenada.toString());

    }

    public static void geraMensagens(ResultSet rs, String nome) throws Exception {
        String nomeArquivo = String.format("arquivosGerados/%sMiniMundo.Mensagens.txt", nome);
        File arquivo = new File(nomeArquivo);
        PrintWriter printWriter = new PrintWriter(arquivo);

        long tInicial, tFinal;
        System.out.println("\nGerando Mensagens...");
        rs.first();
        long cont = 0;
        tInicial = System.currentTimeMillis();
        do {
            long id = rs.getLong("id");
            String autor = rs.getString("autor");
            String tokens = rs.getString("tokens");
            geraVetorMensagem(id, autor, tokens, printWriter);
            if (++cont % 1000 == 0) {
                tFinal = System.currentTimeMillis();
                System.out.printf("%d: %dms %n", cont, (tFinal - tInicial));
                tInicial = tFinal;
                System.gc();
            }
        } while (rs.next());
        printWriter.flush();
        printWriter.close();
    }

    private static void insertMiniMundo(long id, String tokens) {
        try {
            StringTokenizer tokenizer = new StringTokenizer(tokens);
            while (tokenizer.hasMoreTokens()) {
                String token = tokenizer.nextToken();
                Integer frequency = mapTokens.get(token);
                if (frequency == null) {
                    mapTokens.put(token, 1);
                } else {
                    mapTokens.put(token, frequency + 1);
                }
            }
        } catch (NullPointerException ex) {
            branco++;
        }
    }

    private static void geraVetorMensagem(long id, String autor, String tokens, PrintWriter printWriter) throws Exception {
        if (tokens != null) {
            StringTokenizer tokenizer = new StringTokenizer(tokens);
            printWriter.print(id + "," + autor + ",");
            if (!listaAdjacenciaAutor.containsKey(autor)) {
                listaAdjacenciaAutor.put(autor, new HashSet<Integer>());
                mapMensagensAutor.put(autor, new HashSet<Integer>());
            }
            mapMensagensAutor.get(autor).add(new Integer(String.valueOf(id)));
            Set<String> setTokensMensagens = new HashSet<String>();
            List<Integer> listaPosTokens = new ArrayList<Integer>();
            int posicao = -1;
            while (tokenizer.hasMoreTokens()) {
                String token = tokenizer.nextToken();
                if (setTokensMensagens.add(token)) {
                    posicao = Collections.binarySearch(listaOrdenada, token);
                    printWriter.print(posicao);
                    printWriter.print(" ");
                    listaPosTokens.add(posicao);
                }
            }
            printWriter.println();
            listaAdjacenciaAutor.get(autor).addAll(listaPosTokens);
        }
    }

    //FAZER AUTOR POR ID DAS MENSAGENS
    
    public static void agrupaMensagensAutor(String nome) throws Exception {

        String nomeArquivo = String.format("arquivosGerados/%sMiniMundo.AutorOcorrencia.txt", nome);
        File arquivo = new File(nomeArquivo);
        PrintWriter printWriter = new PrintWriter(arquivo);

        for (Entry<String, Set<Integer>> e : listaAdjacenciaAutor.entrySet()) {
            printWriter.print(e.getKey() + ",");
            StringBuilder ocorrencias = new StringBuilder();
            for (Integer i : e.getValue()) {
                ocorrencias.append(i.toString());
                ocorrencias.append(" ");
            }
            printWriter.println(ocorrencias.toString());
        }
        printWriter.flush();
        printWriter.close();
        
        nomeArquivo = String.format("arquivosGerados/%sMiniMundo.AutorMensagens.txt", nome);
        arquivo = new File(nomeArquivo);
        printWriter = new PrintWriter(arquivo);

        for (Entry<String, Set<Integer>> e : mapMensagensAutor.entrySet()) {
            printWriter.print(e.getKey() + ",");
            StringBuilder ocorrencias = new StringBuilder();
            for (Integer i : e.getValue()) {
                ocorrencias.append(i.toString());
                ocorrencias.append(" ");
            }
            printWriter.println(ocorrencias.toString());
        }
        printWriter.flush();
        printWriter.close();
    }
}
